<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="pmxge" id="pmxge"><span data-lake-id="u831a5239" id="u831a5239">典型回答</span></h1>
  <p data-lake-id="ua3430749" id="ua3430749"><span data-lake-id="u8c616b32" id="u8c616b32">在Java的Set体系中，根据实现方式不同主要分为两大类。HashSet和TreeSet。</span></p>
  <p data-lake-id="u59cb116a" id="u59cb116a"><span data-lake-id="u597f6112" id="u597f6112">​</span><br></p>
  <ol list="u4866caaa">
   <li fid="u26f3a257" data-lake-id="u5dbd92dc" id="u5dbd92dc"><span data-lake-id="u59fffa20" id="u59fffa20">TreeSet 是二叉树实现的，TreeSet中的数据是自动排好序的，不允许放入null值；底层基于TreeMap</span></li>
   <li fid="u26f3a257" data-lake-id="u928740f9" id="u928740f9"><span data-lake-id="uca417d7c" id="uca417d7c">HashSet 是哈希表实现的，HashSet中的数据是无序的，可以放入null，但只能放入一个null，两者中的值都不能重复，就如数据库中唯一约束；底层基于HashMap</span></li>
  </ol>
  <p data-lake-id="u4d832351" id="u4d832351"><span data-lake-id="u0bfd1c4b" id="u0bfd1c4b">​</span><br></p>
  <p data-lake-id="u498d519a" id="u498d519a"><span data-lake-id="u616acc8b" id="u616acc8b">在HashSet中，基本的操作都是有HashMap底层实现的，因为HashSet底层是用HashMap存储数据的。当向HashSet中添加元素的时候，首先计算元素的hashCode值，然后通过</span><strong><span data-lake-id="u44b0f427" id="u44b0f427">扰动计算和按位与</span></strong><span data-lake-id="u06f4762c" id="u06f4762c">的方式计算出这个元素的存储位置，如果这个位置为空，就将元素添加进去；如果不为空，则用equals方法比较元素是否相等，相等就不添加，否则找一个空位添加。</span></p>
  <p data-lake-id="uf1e7916e" id="uf1e7916e"><span data-lake-id="ube2ed4fb" id="ube2ed4fb">​</span><br></p>
  <p data-lake-id="u44be91db" id="u44be91db"><span data-lake-id="ue5be09c0" id="ue5be09c0">TreeSet的底层是TreeMap的keySet()，而TreeMap是基于红黑树实现的，红黑树是一种平衡二叉查找树，它能保证任何一个节点的左右子树的高度差不会超过较矮的那棵的一倍。</span></p>
  <p data-lake-id="u28b2baf0" id="u28b2baf0"><span data-lake-id="ufa121b98" id="ufa121b98">​</span><br></p>
  <p data-lake-id="u91c6b119" id="u91c6b119"><span data-lake-id="ub435c447" id="ub435c447">TreeMap是按key排序的，元素在插入TreeSet时compareTo()方法要被调用，所以TreeSet中的元素要实现Comparable接口。TreeSet作为一种Set，它不允许出现重复元素。TreeSet是用compareTo()来判断重复元素的。</span></p>
  <h1 data-lake-id="Z9Cux" id="Z9Cux"><span data-lake-id="u6617a38a" id="u6617a38a">知识扩展</span></h1>
  <h2 data-lake-id="fK4Vh" id="fK4Vh"><span data-lake-id="uc6dac284" id="uc6dac284" style="color: var(--yq-text-primary)">HashSet，TreeSet，LinkedHashSet，BitSet有何区别</span></h2>
  <ol list="uf9cbf709">
   <li fid="ud05858cb" data-lake-id="ube701b92" id="ube701b92"><span data-lake-id="ub1d224f0" id="ub1d224f0">功能不同：HashSet是功能最简单的Set，只提供去重的能力；LinkedHashSet不仅提供去重功能，而且还能记录插入和查询顺序；TreeSet提供了去重和排序的能力；BitSet不仅能提供去重能力，同时也能减少存储空间的浪费，不过对于普通的对象不太友好，需要做额外处理</span></li>
   <li fid="ud05858cb" data-lake-id="u2b0329b4" id="u2b0329b4"><span data-lake-id="uf9865925" id="uf9865925">实现方式不同：HashSet基于HashMap，去重是根据HashCode和equals方法的；LinkedHashSet是基于LinkedHashMap，通过双向链表记录插入顺序；TreeSet是基于TreeMap的，去重是根据compareTo方法的；BitSet基于位数组，一般只用于数字的存储和去重</span></li>
   <li fid="ud05858cb" data-lake-id="u697730e8" id="u697730e8"><span data-lake-id="u614c7506" id="u614c7506">其实BitSet只是叫做Set而已，它既没有实现Collection接口，也和Iterable接口没有什么关系，但是是名字相似而已</span></li>
  </ol>
  <h3 data-lake-id="deT38" id="deT38"><span data-lake-id="u178ed5ac" id="u178ed5ac">什么是BitSet？有什么作用？</span></h3>
  <p data-lake-id="u60b11a23" id="u60b11a23"><span data-lake-id="u12abc996" id="u12abc996">顾名思义，BitSet是位集合，通常来说，位集合的底层的数据结构是一个bit数组，如果第n位为1，则表明数字n在该数组中。</span></p>
  <p data-lake-id="u931b26d2" id="u931b26d2"><span data-lake-id="u2b4d06d4" id="u2b4d06d4">举个例子，如果调用BitSet#set(10)，业务语意是把10放到BitSet中，内部的操作则是通过把二进制的第十位（低位）置为1。这样，就代表BitSet中包含了10这个数字。</span></p>
  <p data-lake-id="u745308bf" id="u745308bf"><span data-lake-id="uebbf5387" id="uebbf5387">不过，对于Java中的BitSet来讲，因为Java不知道bit类型，所以它的底层结构并不是一个bit类型数组，但是也不是一个byte类型数组，而是一个long类型的数组，这样设置的目的是因为long有64位，每次可以读取64位，在进行set或者or操作的时候，for循环的次数会更少，提高了性能。</span></p>
  <p data-lake-id="u932f1aa3" id="u932f1aa3"><span data-lake-id="ub7a200c0" id="ub7a200c0">它最大的好处就是对于多个数字来说，降低了存储空间，如正常情况下，将每一个int类型（32bit）的数字存储到内存中需要 4B * (2^31-1) = 8 GB，但是如果用BitSet的话，就会节省到原来的1/32。</span></p>
  <p data-lake-id="uc8e99309" id="uc8e99309"><span data-lake-id="uc78ddfae" id="uc78ddfae">BitSet常见的使用例子往往和大数相关：</span></p>
  <ol list="uaf1b16d1">
   <li fid="uef1aafad" data-lake-id="u0d7a2df9" id="u0d7a2df9"><span data-lake-id="u26035a46" id="u26035a46">现在有1千万个随机数，随机数的范围在1到1亿之间。求出将1到1亿之间没有在随机数中的数</span></li>
   <li fid="uef1aafad" data-lake-id="u6aab65d8" id="u6aab65d8"><span data-lake-id="u0220b8c7" id="u0220b8c7">统计N亿个数据中没有出现的数据</span></li>
   <li fid="uef1aafad" data-lake-id="uc84dcf35" id="uc84dcf35"><span data-lake-id="u7018bd20" id="u7018bd20">将N亿个不同数据进行排序等</span></li>
  </ol>
  <p data-lake-id="u4141fbc2" id="u4141fbc2"><span data-lake-id="uccef9320" id="uccef9320">但是BitSet也有缺点，譬如集合中存储一些差值比较大的数，如1亿和1两个数，就会导致内存的严重浪费</span></p>
  <p data-lake-id="u2dbcf22f" id="u2dbcf22f"><span data-lake-id="u5af30780" id="u5af30780">​</span><br></p>
  <p data-lake-id="u66c8dc49" id="u66c8dc49"><span data-lake-id="ub897dca5" id="ub897dca5">​</span><br></p>
  <p data-lake-id="u98363871" id="u98363871"><span data-lake-id="u4b37c1fc" id="u4b37c1fc">​</span><br></p>
  <p data-lake-id="u9228d918" id="u9228d918"><span data-lake-id="u78f2936f" id="u78f2936f">​</span><br></p>
  <p data-lake-id="uf2c36f63" id="uf2c36f63"><br></p>
 </body>
</html>